package com.data.boot.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.data.boot.model.GroovyScript;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by dwk on 2019/5/31.
 * 动态加载groovy脚本
 */
@Service
public class ScriptService {
    private static final Log log = LogFactory.get();
    private static final GroovyClassLoader classLoader = new GroovyClassLoader();
    private static Map<String, GroovyScript> scriptCache = new ConcurrentHashMap<>();

    @Value(value = "${root.path}")
    public String rootPath;

    /**
     * 通过文件路径装载groovy脚本
     *
     * @param file
     * @return
     */
    public Script loadScript(String file) {
        FileReader reader = new FileReader(file);
        String scriptText;
        scriptText = reader.readString();
        Script script;
        String key = FileUtil.getName(file);
        String md5 = DigestUtil.md5Hex(scriptText);
        GroovyScript groovyScript;
        if (scriptCache.containsKey(key)) {
            groovyScript = scriptCache.get(key);
            if (groovyScript.getMd5().equals(md5)) {
                script = groovyScript.getScript();//从缓存读取脚本
            } else {
                script = loadScript(scriptText, new Binding());
                groovyScript = new GroovyScript(md5, script);
                scriptCache.put(key, groovyScript);//更新缓存
            }
        } else {
            script = loadScript(scriptText, new Binding());
            groovyScript = new GroovyScript(md5, script);
            scriptCache.put(key, groovyScript);//加入缓存
        }
        return script;
    }

    /**
     * 加载并编译groovy脚本
     *
     * @param scriptText
     * @param binding
     * @return
     */
    public Script loadScript(String scriptText, Binding binding) {
        Script script = null;
        if (scriptText == null) {
            return null;
        }
        try {
            Class scriptClass = classLoader.parseClass(scriptText);
            if (scriptClass != null) {
                log.info("load script:\n" + scriptText + "\nsuccess!");
                return InvokerHelper.createScript(scriptClass, binding);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            classLoader.clearCache();
        }
        return script;
    }

    /**
     * 获取groovy脚本
     *
     * @param scriptName
     * @return
     */
    public Script getScript(String scriptName) {
        GroovyScript groovyScript = scriptCache.get(scriptName);
        if (null == groovyScript) {//首次加载
            return loadScript(rootPath + scriptName);
        } else {
            return groovyScript.getScript();
        }
    }

    /**
     * 卸载groovy脚本
     *
     * @param file
     */
    public void unloadScript(String file) {
        String key = FileUtil.getName(file);
        scriptCache.remove(key);
        log.info("unload script: {} success!", FileUtil.getName(file));
    }
}
